package com.sky.config;

import com.sky.handler.*;
import com.sky.manager.JdbcUserDetailsManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

/**
 * @projectName: netty-cloud
 * @package: com.sky.config
 * @className: WebSecurityConfig
 * @author: zhl
 * @description: 登录方式配置
 * @EnableMethodSecurity : 基于方法的授权，在方法上添加注解 @PreAuthorize("hasAuthority('访问标识')")
 * springsecurity 所有的拦截器都在 DefaultSecurityFilterChain 类中注册
 * @date: 2024/4/23 15:43
 * @version: 1.0
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class WebSecurityConfig {

    @Bean
    @ConditionalOnMissingClass(value = "com.alibaba.druid.pool.DruidDataSource")
    public UserDetailsService userDetailsInMemoryService(){
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        userDetailsManager.createUser(User
                // 使用默认的密码加密方案
                .withDefaultPasswordEncoder()
                .username("admin")
                .password("123456")
                .roles("USER").build());
        return userDetailsManager;
    }

    @Bean
    @ConditionalOnClass(name = "com.alibaba.druid.pool.DruidDataSource")
    public UserDetailsService userDetailsService(){
        return new JdbcUserDetailsManager();
    }


    /** springSecurity(过滤器链) 会默认添加的配置 **/
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{
        /** 授权配置 **/
        httpSecurity
                // 用户认证的过滤器（开启认证保护）
                .authorizeRequests(authorize -> authorize
                        // 拥有 "USER" 资源标识权限的用户才能访问 "/file/getFile" 资源
                        //.requestMatchers("/file/getFile").hasAnyAuthority("USER")
                        // 拥有 "ADMIN" 角色的用户才能访问 "/file“ 路径下面的所有资源
                        //.requestMatchers("/file/**").hasRole("ADMIN")
                        // 对（非上述配置的）所有请求开启认证保护
                        .anyRequest()
                        // 已认证的请求会被自动授权
                        .authenticated()
                );

        /** 登录配置 **/
        // 使用表单授权方式（使用默认的登录、登出页面）
        //.formLogin(Customizer.withDefaults())
        // 使用自定义登录页面
        httpSecurity.formLogin(form ->{
            form.loginPage("/login")
                    // 配置表单提交的参数名，默认值为 username
                    .usernameParameter("userName")
                    // 配置表单提交的参数名，默认值为 password
                    .passwordParameter("password")
                    // 登录失败的跳转页面,"error"为默认值，可以修改
                    .failureForwardUrl("/login?error")
                    // 无需授权即可访问登录页
                    .permitAll()
                    // 登录成功时的处理器
                    .successHandler(new MyAuthenticationSuccessHandler())
                    // 登录失败时的处理器
                    .failureHandler(new MyAuthenticationFailureHandler())
            ;
        })
        // 使用基本的授权方式(如果两种方式都存在，表单优先级更高)
        .httpBasic(Customizer.withDefaults());

        /** 登录注销配置 **/
        httpSecurity.logout(logout -> {
            logout.logoutSuccessHandler(new MyLogoutSuccessHandler());
        });

        /** 请求异常处理配置 **/
        httpSecurity.exceptionHandling(exception -> {
            // 请求用户未认证（未登录）
            exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());
            // 请求资源未授权
            exception.accessDeniedHandler(new MyAccessDeniedHandler());
        });

        /** 同一账号多端（多设备）登录配置 **/
        httpSecurity.sessionManagement(session -> {
            // 最多可以同时登录的设备
            int max = 1;
            // 设置最多几个账号可以同时登录，后登陆的账号会把之前的账号挤掉
            session.maximumSessions(max).expiredSessionStrategy(new MySessionInformationExpiredStrategy());
        });

        /** 开启全局的跨域访问请求 **/
        httpSecurity.cors(Customizer.withDefaults());

        /** 关闭“跨站攻击”防御功能 **/
        httpSecurity.csrf(csrf -> csrf.disable());

        return httpSecurity.build();
    }
}
